home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 329_01 / dprintf.c < prev    next >
Text File  |  1980-01-01  |  16KB  |  588 lines

  1. /*  >  DPRINTF.C
  2.  *
  3.  *  dprintf -- Source Code
  4.  *  (C)  April 4  1990  Asaf Arkin
  5.  *  All rights reserved
  6.  */
  7.  
  8.  
  9.  
  10. /*  Include files:
  11.  */
  12.  
  13. #include  <ctype.h>
  14. #include  <setjmp.h>
  15. #include  <stdarg.h>
  16. #include  <stdio.h>
  17. #include  <stdlib.h>
  18. #include  <string.h>
  19.  
  20.  
  21.  
  22. /*  Macro constants:- TRUE/FALSE, Flags mask bits, and argument size types.
  23.  *  Macros:- Maximum and Minimum expand to yield the maximum or minimum of
  24.  *  two expressions; ToValue gives the decimal value of an ASCII digit and
  25.  *  ToDigit returns a digit from a value in any radix.
  26.  *  N.B.: It goes without saying that the macro parameters must have no side
  27.  *  effects, as they will be carried out more than once.
  28.  */
  29.  
  30. #define  TRUE  1
  31. #define  FALSE 0
  32.  
  33. #define  MaskJustify   0x01  /*  -  Left justify value within field    */
  34. #define  MaskPlusSign  0x02  /*  +  Precede positive value with plus   */
  35. #define  MaskSpace     0x04  /*  sp Precede positive value with space  */
  36. #define  MaskZeros     0x08  /*  0  Justify value with zeros           */
  37. #define  MaskVariant   0x10  /*  #  Output value in variant format     */
  38.  
  39. #define  TypeNormal  1  /*  int/double           */
  40. #define  TypeShort   2  /*  short (meaningless)  */
  41. #define  TypeLong    3  /*  long int             */
  42. #define  TypeDouble  4  /*  long double          */
  43.  
  44. #define  Maximum(a,b)  ((a)>(b)?(a):(b))
  45. #define  Minimum(a,b)  ((a)<(b)?(a):(b))
  46. #define  ToValue(a)    ((a)-'0')
  47. #define  ToDigit(a)    ((a)<10?(a)+'0':(a)-10+'A')
  48.  
  49.  
  50.  
  51. /*  OutFunc (of type dprintf_fp) points to a putchar-like function, which
  52.  *  performs all output. Called with a character int as parameter, the
  53.  *  function returns EOF only if an output error occured.
  54.  */
  55.  
  56. typedef  int (*dprintf_fp)(int);
  57. static dprintf_fp  OutFunc;
  58.  
  59.  
  60. /*  Function declarations:
  61.  */
  62.  
  63. int  dprintf(dprintf_fp, const char *, ...);
  64. int  vdprintf(dprintf_fp, const char *, va_list);
  65. static void  PrintDecimal(long, int, int, char, int *);
  66. static void  PrintRadix(unsigned long, int, int, char, char, int *);
  67. static void  PrintFloat(long double, int, int, char, char, int *);
  68. static void  Print(char *, char *, int, int, char, int *);
  69. static int  ToInteger(char **, unsigned long, int, int);
  70. static void  dputc(int);
  71.  
  72.  
  73. /*  dputc employs this longjmp buffer in the event of an output error.
  74.  */
  75.  
  76. jmp_buf  dputc_Buf;
  77.  
  78.  
  79.  
  80. /*  int  dprintf(dprintf_fp, const char *, ...)
  81.  *
  82.  *  dprintf accepts pointers to a putchar-like function and a format string.
  83.  *  It then passes them to vdprintf, along with a pointer to the variable
  84.  *  arguments list.
  85.  */
  86.  
  87. int  dprintf(dprintf_fp Func, const char *Format, ...)
  88. {
  89.   int  Return;
  90.   va_list  Args;
  91.  
  92.   va_start(Args,Format);
  93.   Return=vdprintf(Func,Format,Args);
  94.   va_end(Args);
  95.   return  Return;
  96. }
  97.  
  98.  
  99.  
  100. /*  int  vdprintf(dprintf_fp, const char *, va_list)
  101.  *
  102.  *  vdprintf is an implementation of vprintf, as defined in the ANSI
  103.  *  standard, with an additional pointer-to-function as its first parameter.
  104.  *  On exit, vdprintf returns the number of characters successfully printed,
  105.  *  EOF if an error occured.
  106.  */
  107.  
  108. int  vdprintf(dprintf_fp Func, const char *Format, va_list Args)
  109. {
  110.   char  Flags,  Size,  *Ptr;
  111.   int  Width,  Precis,  OutCnt = 0;
  112.   long  Int;
  113.   unsigned long  UnsgInt;
  114.   long double  Float;
  115.   char  *FlagsList = "-+ 0#",  *TypesList = "hlL";
  116.  
  117.   /*  The pointer-to-function assigns to static variable OutFunc, rather than
  118.    *  being passed as parameter through three layers of functions. The
  119.    *  longjmp buffer is then initialized, so dputc can return in the event of
  120.    *  an output error.
  121.    */
  122.   OutFunc=Func;
  123.   if (setjmp(dputc_Buf))
  124.     return  EOF;
  125.   /*  The format string is scanned a character at a time: %'s are processed,
  126.    *  all other characters are merely echoed to the output.
  127.    */
  128.   for (; *Format; ++Format)
  129.   {
  130.     if (*Format!='%')
  131.     {
  132.       dputc(*Format);
  133.       ++OutCnt;
  134.       continue;
  135.     }
  136.     /*  An output format can start with a combination of five flags: - + spc
  137.      *  0 #. Flags is set accordingly.
  138.      */
  139.     if (!*++Format)
  140.       return  EOF;
  141.     Flags=0;
  142.     while ((Ptr=strchr(FlagsList,*Format))!=NULL)
  143.     {
  144.       Flags|=1<<(Ptr-FlagsList);
  145.       ++Format;
  146.     }
  147.     /*  Read width (zero assumed, if absent) and precision (-1 assumed, if
  148.      *  absent): width must not start with a zero; precision precedes with a
  149.      *  period -- if no precision follows the period, zero is assumed; an int
  150.      *  argument is consumed for the width or precision, if an * replaces the
  151.      *  value of either.
  152.      */
  153.     Width=0;
  154.     if (*Format=='*')
  155.     {
  156.       Width=va_arg(Args,int);
  157.       if (Width<0)
  158.       {
  159.     Width=-Width;
  160.     Flags|=MaskJustify;
  161.       }
  162.       ++Format;
  163.     }
  164.     else
  165.     while (isdigit(*Format))
  166.       Width=Width*10+ToValue(*Format++);
  167.     if (*Format=='.')
  168.     {
  169.       Precis=0;
  170.       if (*++Format=='*')
  171.       {
  172.         Precis=va_arg(Args,int);
  173.         ++Format;
  174.       }
  175.       else
  176.       while (isdigit(*Format))
  177.         Precis=Precis*10+ToValue(*Format++);
  178.     }
  179.     else
  180.       Precis=-1;
  181.     /*  An argument is either int (default), short int ('h' -- meaningless),
  182.      *  long int ('l'), double (default float), or long double ('L').
  183.      */
  184.      if ((Ptr=strchr(TypesList,*Format))!=NULL)
  185.      {
  186.        Size=Ptr-TypesList+TypeShort;
  187.        ++Format;
  188.      }
  189.      else
  190.        Size=TypeNormal;
  191.     /*  Consume one output format letter.
  192.      *  Auxiliary functions process most formats, keeping vdprintf short, or
  193.      *  else it may fail to compile.
  194.      */
  195.     switch (*Format)
  196.     {
  197.       case 'd':
  198.       case 'i':
  199.         if (Size==TypeLong)
  200.           Int=va_arg(Args,long);
  201.         else
  202.           Int=va_arg(Args,int);
  203.         PrintDecimal(Int,Width,Precis,Flags,&OutCnt);
  204.         break;
  205.       case 'u':
  206.       case 'o':
  207.       case 'x': case 'X':
  208.         if (Size==TypeLong)
  209.           UnsgInt=va_arg(Args,unsigned long);
  210.         else
  211.           UnsgInt=va_arg(Args,unsigned);
  212.         PrintRadix(UnsgInt,Width,Precis,Flags,*Format,&OutCnt);
  213.         break;
  214.       case 'c':
  215.       {
  216.         static char  Char[2]= {0,0};
  217.  
  218.     /*  chars are promoted to int when passed to functions.
  219.      */
  220.     Char[0]=va_arg(Args,int);
  221.         Print(Char,NULL,Width,-1,Flags,&OutCnt);
  222.         break;
  223.       }
  224.       case 's':
  225.     /*  If pointer is null, print string "{NULL}".
  226.      */
  227.     Ptr=va_arg(Args,char *);
  228.     if (Ptr==NULL)
  229.       Ptr="{NULL}";
  230.         Print(Ptr,NULL,Width,Precis,Flags,&OutCnt);
  231.         break;
  232.       case 'f':
  233.       case 'e': case 'E':
  234.       case 'g': case 'G':
  235.         if (Size==TypeDouble)
  236.           Float=va_arg(Args,long double);
  237.         else
  238.           Float=va_arg(Args,double);
  239.         PrintFloat(Float,Width,Precis,Flags,*Format,&OutCnt);
  240.         break;
  241.       case 'p':
  242.         /*  The pointer-to-void argument is cast to long unsigned, assuming
  243.          *  pointer representation to remain intact.
  244.          */
  245.         UnsgInt=(unsigned long) va_arg(Args,void *);
  246.         PrintRadix(UnsgInt,Width,Precis,Flags,*Format,&OutCnt);
  247.         break;
  248.       case 'n':
  249.         *(va_arg(Args,int *))=OutCnt;
  250.         break;
  251.       case '%':
  252.         Print("%",NULL,Width,-1,Flags,&OutCnt);
  253.         break;
  254.       default:
  255.         return EOF;
  256.     }
  257.   }
  258.   return  OutCnt;
  259. }
  260.  
  261.  
  262.  
  263. /*  void  PrintDecimal(long, int, int, char, int *)
  264.  *
  265.  *  Print a decimal value (%d or %i) with a sign prefix.
  266.  */
  267.  
  268. static void  PrintDecimal(long Int, int Width, int Precis, char Flags,
  269.                           int *OutCnt)
  270. {
  271.   char  *Prefix;
  272.   char  *Buffer;
  273.  
  274.   if(Int<0)
  275.   {
  276.     Int=-Int;
  277.     Prefix="-";
  278.   }
  279.   else
  280.   {
  281.     if (Flags&MaskPlusSign)
  282.       Prefix="+";
  283.     else
  284.     if (Flags&MaskSpace)
  285.       Prefix=" ";
  286.     else
  287.       Prefix=NULL;
  288.   }
  289.   ToInteger(&Buffer,Int,10,Precis);
  290.   Print(Buffer,Prefix,Width,-1,Flags,OutCnt);
  291.   free(Buffer);
  292. }
  293.  
  294.  
  295. /*  void  PrintRadix(unsigned long, int, int, char, char, int *)
  296.  *
  297.  *  Print an unsigned int in decimal (%u), octal (%o), hexadecimal (%x or
  298.  *  %X), or pointer (%p) representation. In the variant fo